package com.gitee.sparta.common.helpers;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.gitee.sparta.common.exception.SpartaException;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Map;

/**
 * 序列化工具
 *
 * @author zyred
 * @since v 0.0.1
 */
@Slf4j
public class JacksonHelper {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static final String CLASS_KEY = "@class";

    static {
        MAPPER.activateDefaultTypingAsProperty(
                // 设置信任的包及类型
                BasicPolymorphicTypeValidator.builder()
                        .allowIfBaseType("java.")
                        .allowIfBaseType("com.")
                        .allowIfBaseType("org.")
                        .allowIfBaseType("cn.")
                        .allowIfSubType(Collection.class)
                        .allowIfSubType(Map.class)
                        .allowIfSubType(Number.class)
                        .allowIfSubTypeIsArray().build(),
                ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT,
                CLASS_KEY
        );
        MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 定义时间的处理
        MAPPER.registerModules(JacksonHelper.getTimeModule());
        MAPPER.findAndRegisterModules();
    }

    public static String serialize(Object obj) {
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (Throwable t) {
            throw new SpartaException(t);
        }
    }

    @SuppressWarnings("all")
    public static <T> T deserialize(String jsonString, Class<T> clazz) {
        try {
            return MAPPER.readValue(jsonString, clazz);
        } catch (Throwable t) {
            log.info("json string :{}", jsonString);
            log.error("[jackson deserialize] exception : {}", t.getMessage(), t);
            if (t instanceof InvalidDefinitionException) {
                InvalidDefinitionException ex = (InvalidDefinitionException) t;
                throw new SpartaException(ex.getType().getTypeName() + "类中无默认的无参构造器.");
            }
            throw new SpartaException(t);
        }
    }

    private static JavaTimeModule getTimeModule () {
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        return timeModule;
    }
}
